#include "StdAfx.h"
#include "Ceworld.h"


int GET_UP_DIM( int pos, int world_width, int world_height ) { return pos < world_width ? pos - world_width + world_width*world_height : pos - world_width; }
int GET_DOWN_DIM( int pos, int world_width, int world_height ) { return pos + world_width >= world_width*world_height ? pos + world_width - world_width*world_height : pos + world_width; }
int GET_LEFT_DIM( int pos, int world_width, int world_height ) { return pos % world_width == 0 ? pos - 1 + world_width : pos - 1; }
int GET_RIGHT_DIM( int pos, int world_width, int world_height ) { return (pos+1) % world_width == 0 ? pos + 1 - world_width : pos + 1; }
int GET_LEFTUP_DIM( int pos, int world_width, int world_height ) { return GET_LEFT_DIM( GET_UP_DIM( pos, world_width, world_height ), world_width, world_height ); }
int GET_RIGHTUP_DIM( int pos, int world_width, int world_height ) { return GET_RIGHT_DIM( GET_UP_DIM( pos, world_width, world_height ), world_width, world_height ); }
int GET_LEFTDOWN_DIM( int pos, int world_width, int world_height ) { return GET_LEFT_DIM( GET_DOWN_DIM( pos, world_width, world_height ), world_width, world_height ); }
int GET_RIGHTDOWN_DIM( int pos, int world_width, int world_height ) { return GET_RIGHT_DIM( GET_DOWN_DIM( pos, world_width, world_height ), world_width, world_height ); }

Ceworld::Ceworld(void)
{
	m_map = NULL;
}


Ceworld::~Ceworld(void)
{
	if( m_map )
	{
		for( int i=0; i<m_mapsize; i++ )
			delete m_map[i];
		delete m_map;
	}
}

void Ceworld::DestoryWorld( void )
{
	if( m_map )
	{
		for( int i=0; i<m_mapsize; i++ )
			delete m_map[i];
		delete m_map;
		m_map = NULL;
	}
	m_listCreature.Clear();
}

bool Ceworld::CreateWorld( CEnvironment* env )
{
	int i=0;
	p_env = env;
	m_mapsize = env->m_widthWorld*env->m_heightWorld;

	//1. create all water world
	m_map = new CPosition* [m_mapsize];
	for( i=0; i<m_mapsize; i++ )
	{
		m_map[i] = new CWater( env );
		m_map[i]->SetPosition( i/env->m_widthWorld, i%env->m_widthWorld );
	}

	//2. random create creature, alound by food
	for( i=0; i<env->m_numInitCreature; i++ )
	{
		//int pos = (((double) rand() / 
        //                 (double)( env->m_widthWorld*env->m_heightWorld)) * env->m_widthWorld*env->m_heightWorld + 0);
		int pos = i* ( env->m_widthWorld*env->m_heightWorld/env->m_numInitCreature );

		CPosition *pSelectedNode = m_map[pos];
		/*if( pSelectedNode->GetType() == POSTYPE_CREATURE )
		{
			i--;
			continue;
		}*/
		
		delete pSelectedNode;
		CCreature *pNewCreature = new CCreature( env, NULL, NULL, NULL );
		pNewCreature->SetPosition( pos/env->m_widthWorld, pos%env->m_widthWorld );
		m_map[pos] = pNewCreature;
		m_listCreature.AddTail( pNewCreature );

		for( int j=0; j<8; j++ )
		{
			int newpos;
			switch( j )
			{
			case 0:
				newpos = GET_UP_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 1:
				newpos = GET_DOWN_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 2:
				newpos = GET_LEFT_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 3:
				newpos = GET_RIGHT_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 4:
				newpos = GET_LEFTUP_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 5:
				newpos = GET_RIGHTUP_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 6:
				newpos = GET_LEFTDOWN_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			case 7:
				newpos = GET_RIGHTDOWN_DIM( pos, env->m_widthWorld, env->m_heightWorld );
				break;
			}

			pSelectedNode = m_map[newpos];
			if( pSelectedNode->GetType() != POSTYPE_CREATURE )
			{
				delete pSelectedNode;
				CFood *pNewFood = new CFood( env );
				m_map[newpos] = pNewFood;
				pNewFood->SetPosition( newpos/env->m_widthWorld, newpos%env->m_widthWorld );
			}
		}

	}

	return true;
}

bool Ceworld::RunOneStep( void )
{
	//do move and status change
	for( int i=0; i<p_env->m_widthWorld*p_env->m_heightWorld; i++ )
	{
		CPosition* pSelectedNode = m_map[i];
		if( !pSelectedNode->HasMoved() )
		{
			int newpos=i;
			pSelectedNode->PrepareMove();
			CRoundNodes nodes;
			GetRoundNodes( i, nodes );
			DIRECTION dir = pSelectedNode->QueryNextMove( nodes );

			switch( dir )
			{
			case DIR_UP:
				newpos = GET_UP_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_LEFT:
				newpos = GET_LEFT_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_RIGHT:
				newpos = GET_RIGHT_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_DOWN:
				newpos = GET_DOWN_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_LEFT_UP:
				newpos = GET_LEFTUP_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_RIGHT_UP:
				newpos = GET_RIGHTUP_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_LEFT_DOWN:
				newpos = GET_LEFTDOWN_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			case DIR_RIGHT_DOWN:
				newpos = GET_RIGHTDOWN_DIM( i, p_env->m_widthWorld, p_env->m_heightWorld );
				break;
			default:
				break;
			}
			
			if( newpos != i )
			{
				if( !MoveCreature( i, newpos ) )
				{
					//move fail, stay old position
					newpos = i;
				}
			}

			POSTYPE tp = pSelectedNode->GetNextChange();
			if( tp != pSelectedNode->GetType() )
				ReplacePosition( newpos, tp );
		}
	}
#if 1
	//breed
	for( int i=0; i<p_env->m_widthWorld*p_env->m_heightWorld; i++ )
	{
		CCreature* pFather = m_map[i]->GetType()==POSTYPE_CREATURE ? (CCreature*)m_map[i] : NULL;
		if( pFather && pFather->IsMature() )
		{
			//find if another mature creature is near
			CCreature* listMature[8];
			int nMatureCount = FindNearbyMatureCreature( i, listMature );

			if( nMatureCount == 0 )
			{
				//breed without sex
				int nChildPos = FindChildPos( i );
				if( nChildPos >= 0 )
				{
					CCreature* pChild = new CCreature( p_env, NULL, pFather, NULL );
					delete m_map[nChildPos];
					m_map[nChildPos] = pChild;
					pChild->SetPosition( nChildPos/p_env->m_widthWorld, nChildPos%p_env->m_widthWorld );
					m_listCreature.Insert( pFather, pChild );
					pFather->DoBreed();
				}
			}
			else
			{
				//breed with sex
				for( int j=0; j<nMatureCount; j++ )
				{
					CCreature* pMother = listMature[j];
					int nChildPos = FindChildPos( i );
					if( nChildPos >= 0 )
					{
						CCreature* pChild = new CCreature( p_env, NULL, pFather, pMother );
						delete m_map[nChildPos];
						m_map[nChildPos] = pChild;
						pChild->SetPosition( nChildPos/p_env->m_widthWorld, nChildPos%p_env->m_widthWorld );
						m_listCreature.Insert( pFather, pChild );
						pFather->DoBreed();
						pMother->DoBreed();
					}
				}
			}
		}
	}
#endif
	//end move
	for( int i=0; i<p_env->m_widthWorld*p_env->m_heightWorld; i++ )
	{
		m_map[i]->EndMove();
	}

	return true;
}

bool Ceworld::MoveCreature( int oldpos, int newpos )
{
	CPosition* pCreature = m_map[oldpos];

	if( m_map[newpos]->GetType() != POSTYPE_CREATURE )
	{
		pCreature->SetMoveResult( m_map[newpos]->GetType() );
		delete m_map[newpos];
		m_map[newpos] = pCreature;
		pCreature->SetPosition( newpos/p_env->m_widthWorld, newpos%p_env->m_widthWorld );

		CWater* pwt = new CWater( p_env );
		m_map[oldpos] = pwt;
		pwt->SetPosition( oldpos/p_env->m_widthWorld, oldpos%p_env->m_widthWorld );
		return true;
	}
	else
	{
		//could not move to other creature!
		pCreature->SetMoveResult( m_map[newpos]->GetType() );
		return false;
	}
}

void Ceworld::ReplacePosition( int pos, POSTYPE newType )
{
	CPosition* pNew;
	switch( newType )
	{
	case POSTYPE_WATER:
		pNew = new CWater( p_env );
		break;
	case POSTYPE_FOOD:
		pNew = new CFood( p_env );
		break;
	case POSTYPE_POISON:
		pNew = new CPoison( p_env );
		break;
	case POSTYPE_CREATURE:
		pNew = new CCreature( p_env, NULL, NULL, NULL );
		break;
	}

	if(  m_map[pos]->GetType() == POSTYPE_CREATURE )
		m_listCreature.Detach( (CCreature*)m_map[pos] );
	delete m_map[pos];
	m_map[pos] = pNew;
	pNew->SetPosition( pos/p_env->m_widthWorld, pos%p_env->m_widthWorld );
}

bool Ceworld::Save( char* fileName )
{
	return false;
}

bool Ceworld::Load( char* fileName )
{
	return false;
}

POSTYPE Ceworld::GetPosType( int x, int y )
{
	return m_map[x+y*this->p_env->m_widthWorld]->GetType();
}

void Ceworld::GetCreatureInfo( int& nCount, int& nMaxAge, int& nMinAge, int& nMaxLife, int& nMinLife, int& nMaxBreed, int& nMinBreed )
{
	nCount = 0;
	nMaxAge = 0;
	nMinAge = 1000000;
	nMaxLife = 0;
	nMinLife = 1000000;
	nMaxBreed = 0;
	nMinBreed = 1000000;
	m_listCreature.SetToFirst();

	CCreature* pCreature;
	while( pCreature=m_listCreature.GetNext() )
	{
		if( nMaxLife < pCreature->GetLife() )
			nMaxLife = pCreature->GetLife();
		if( nMinLife > pCreature->GetLife() )
			nMinLife = pCreature->GetLife();
		if( nMaxAge < pCreature->GetAge() )
			nMaxAge = pCreature->GetAge();
		if( nMinAge > pCreature->GetAge() )
			nMinAge = pCreature->GetAge();
		if( nMaxBreed < pCreature->GetBreedCount() )
			nMaxBreed = pCreature->GetBreedCount();
		if( nMinBreed > pCreature->GetBreedCount() )
			nMinBreed = pCreature->GetBreedCount();
		nCount++;
	}
}

void Ceworld::GetRoundNodes( int indexOfMap, CRoundNodes& nodes )
{
	int newpos;
	for( int dir=DIR_UP; dir<=DIR_RIGHT_DOWN; dir++ )
	{
		switch( dir )
		{
		case DIR_UP:
			newpos = GET_UP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT:
			newpos = GET_LEFT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT:
			newpos = GET_RIGHT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_DOWN:
			newpos = GET_DOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_UP:
			newpos = GET_LEFTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_UP:
			newpos = GET_RIGHTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_DOWN:
			newpos = GET_LEFTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_DOWN:
			newpos = GET_RIGHTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		default:
			break;
		}
		nodes.SetTypeOfDir( (DIRECTION)dir, m_map[newpos]->GetType() );
	}
}

int Ceworld::FindNearbyMatureCreature( int indexOfMap, CCreature** listMature )
{
	int newpos;
	int nMatureCount = 0;
	for( int dir=DIR_UP; dir<=DIR_RIGHT_DOWN; dir++ )
	{
		switch( dir )
		{
		case DIR_UP:
			newpos = GET_UP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT:
			newpos = GET_LEFT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT:
			newpos = GET_RIGHT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_DOWN:
			newpos = GET_DOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_UP:
			newpos = GET_LEFTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_UP:
			newpos = GET_RIGHTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_DOWN:
			newpos = GET_LEFTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_DOWN:
			newpos = GET_RIGHTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		default:
			break;
		}
		if( m_map[newpos]->GetType() == POSTYPE_CREATURE && ((CCreature*)m_map[newpos])->IsMature() )
			listMature[nMatureCount++] =  (CCreature*)m_map[newpos];
	}
	return nMatureCount;
}

int Ceworld::FindChildPos( int indexOfMap )
{
	int newpos;
	for( int dir=DIR_UP; dir<=DIR_RIGHT_DOWN; dir++ )
	{
		switch( dir )
		{
		case DIR_UP:
			newpos = GET_UP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT:
			newpos = GET_LEFT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT:
			newpos = GET_RIGHT_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_DOWN:
			newpos = GET_DOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_UP:
			newpos = GET_LEFTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_UP:
			newpos = GET_RIGHTUP_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_LEFT_DOWN:
			newpos = GET_LEFTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		case DIR_RIGHT_DOWN:
			newpos = GET_RIGHTDOWN_DIM( indexOfMap, p_env->m_widthWorld, p_env->m_heightWorld );
			break;
		default:
			break;
		}

		if( m_map[newpos]->GetType() != POSTYPE_CREATURE )
			return newpos;
	}
	return -1;
}

void CCreature::BreedWithSex( CCreature* pFather, CCreature* pMother )
{
}

void CCreature::BreedWithoutSex( CCreature* pFather )
{
}

void CCreature::InitBrain( CBrain* pBrain )
{
}

DIRECTION CCreature::QueryNextMove( CRoundNodes nodes )
{
	return DIR_RIGHT_UP;//(DIRECTION)(int)( (rand() % DIR_RIGHT_DOWN) +1 );//DIR_LEFT_UP;//DIR_NONE;
}

